# 签名SH256WITHRSA
import json
import datetime
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from base64 import decodebytes, encodebytes
from urllib.parse import quote_plus


class Alipay:
    """支付宝接口，PC端支付接口"""

    def __init__(self, appid, app_notify_url, app_private_key_path,
                 alipay_public_key_path, return_url):
        self.appid = appid
        self.app_notify_url = app_notify_url
        self.app_private_key_path = app_private_key_path
        self.app_private_key = None
        self.alipay_public_key_path = alipay_public_key_path
        self.return_url = return_url
        with open(self.app_private_key_path) as fp:
            self.app_private_key = RSA.importKey(fp.read())
        with open(self.alipay_public_key_path) as fp:
            self.alipay_public_key = RSA.importKey(fp.read())

    def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):
        biz_content = {
            'out_trade_no': out_trade_no,
            'product_code': "FAST_INSTANT_TRADE_PAY",
            'total_amount': total_amount,
            'subject': subject,
        }
        biz_content.update(kwargs)
        # print('biz_content',biz_content)
        data = self.build_body('alipay.trade.page.pay', biz_content, self.return_url)
        # print('data', data)
        return self.sign_data(data)

    def build_body(self, method, biz_content, return_url=None):
        data = {
            'app_id': self.appid,
            'method': method,
            'format': "JSON",
            'return_url': return_url,
            'charset': "utf-8",
            'sign_type': 'RSA2',
            'timestamp': datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            'version': "1.0",
            'biz_content': biz_content
        }
        if return_url is not None:
            data["notify_url"] = self.app_notify_url
            data["return_url"] = self.return_url
        return data

    def sign_data(self, data):
        data.pop("sign", None)
        # 排序后的字符串
        unsigned_items = self.ordered_data(data)
        # print('unsigned_items', unsigned_items)
        # 获取签名的字符串
        unsigned_string = "&".join(["{0}={1}".format(k, data[k]) for k in sorted(unsigned_items)])
        sign = self.sign(unsigned_string.encode("utf-8"))
        quote_string = "&".join(["{0}={1}".format(k, quote_plus(data[k])) for k in sorted(unsigned_items)])
        signed_string = quote_string + "&sign=" + quote_plus(sign)
        return signed_string

    def ordered_data(self, data):
        complex_keys = []
        for key, value in data.items():
            if isinstance(value, dict):
                complex_keys.append(key)
        print("complex_keys", complex_keys)
        # 将字典类型的数据dump出来
        for key in complex_keys:
            data[key] = json.dumps(data[key], separators=(",", ":"))
            print(data[key])

        return data

    def sign(self, unsigned_string):
        # 开始计算签名
        key = self.app_private_key
        signer = PKCS1_v1_5.new(key)
        signature = signer.sign(SHA256.new(unsigned_string))
        # base64编码，转换为字符串
        sign = encodebytes(signature).decode("utf-8").replace("\n", "")
        return sign

    # 支付宝校验
    def _verify(self, raw_content, signature):
        # 开始计算签名
        key = self.alipay_public_key
        signer = PKCS1_v1_5.new(key)
        digest = SHA256.new()
        digest.update(raw_content.encode("utf-8"))
        if signer.verify(digest, decodebytes(signature.encode("utf8"))):
            return True
        return False

    def verify(self, data, signature):
        if "sign_type" in data:
            sign_type = data.pop("sign_type")
        # 排序后的字符串
        unsigned_items = self.ordered_data(data)
        message = "&".join(["{0}={1}".format(k, data[k]) for k in sorted(unsigned_items)])
        return self._verify(message, signature)
